﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SewerAPI.Core;
using System.Collections.Specialized;
using SewerAPI.Util;

namespace SewerAPI.IO
{
    /// <summary>
    /// Delegate to hook up just before the transaction
    /// </summary>
    /// <param name="m"></param>
    public delegate void PreTransactionHandler(Message m);

    /// <summary>
    /// Delegate to hook up after a failed transaction
    /// </summary>
    /// <param name="m"></param>
    /// <param name="e"></param>
    public delegate void FailedTransactionHandler(Message m, TransactionArgs e);

    /// <summary>
    /// Delegate to hook up after a succeeded transaction
    /// </summary>
    /// <param name="m"></param>
    /// <param name="e"></param>
    public delegate void SuccesTransactionHandler(Message m, TransactionArgs e);

    /// <summary>
    /// Delegate to hook up when bytes are received
    /// </summary>
    /// <param name="received"></param>
    public delegate void BytesReceived(byte[] received, IAsyncResult result);

    
    /// <summary>
    /// Basic communication component. Implements a basic structure with event
    /// handlers to transact a message. Will be used in both server as client.
    /// </summary>
    public abstract class TransactionHandler
    {
        #region Private Fields

        event PreTransactionHandler onpretransaction;
        event SuccesTransactionHandler onsuccess;
        event FailedTransactionHandler onfail;
        
        #endregion

        #region Public Fields

        /// <summary>
        /// This event handler is executed just before the transaction
        /// </summary>
        public event PreTransactionHandler OnPreTransaction
        {
            add { onpretransaction += value; }
            remove { onpretransaction -= value; }
        }

        /// <summary>
        /// This event handler is executed after a succesful transaction
        /// </summary>
        public event SuccesTransactionHandler OnSucces
        {
            add { onsuccess += value; }
            remove { onsuccess -= value; }
        }

        /// <summary>
        /// This event handler is executed after a failed transaction
        /// </summary>
        public event FailedTransactionHandler OnFail
        {
            add { onfail += value; }
            remove { onfail -= value; }
        }

        #endregion

        #region Constructor

        public TransactionHandler()
        {
            try
            {
                InitializeHandler();
            }
            catch (Exception e)
            {
                string name = this.GetType().Name;
                SewerLogger.Instance.Error(string.Format("Cannot initialize {0}", name), e);
            }
        }

        #endregion

        #region Public Methods

        public void Transact(Message m) 
        {
            //Create the arguments
            TransactionArgs args = new TransactionArgs();
            args.SetState(MessageTransactionState.PreTransaction);
            try
            {
                //Run the pre transaction delegates
                if (onpretransaction != null) onpretransaction(m);
                args.SetState(MessageTransactionState.Transaction);
                //Run the actual transaction
                PerformTransaction(m);
                args.SetState(MessageTransactionState.PostTransaction);
                //Run the succes transaction delegates 
                if (onsuccess != null)
                {
                    onsuccess(m, args);
                }
            }
            catch (Exception e1)
            {
                //Log it for debugging purposes, actual error logging should be handled
                //By the delegates itself
                SewerLogger.Instance.Debug("Message transaction failed: " + e1.Message);
                if (onfail != null)
                {
                    try
                    {
                        //Run the onfail delegates
                        onfail(m, args);
                    }
                    catch (Exception e2)
                    {
                        SewerLogger.Instance.Error("One of the OnFail delegates failed itself", e2);
                    }
                }
            }
        }

        #endregion

        #region Abstract Methods

        /// <summary>
        /// Perform the actual transaction
        /// </summary>
        /// <param name="m"></param>
        protected abstract void PerformTransaction(Message m);

        protected abstract void InitializeHandler();

        #endregion
    }

    /// <summary>
    /// Transaction arguments
    /// </summary>
    public class TransactionArgs
    {
        #region Private Fields

        string comments = string.Empty;
        NameValueCollection parameters = new NameValueCollection();
        MessageTransactionState state = MessageTransactionState.Unknown;
        Exception exception;

        #endregion

        #region Public Fields

        /// <summary>
        /// Room for extra comments
        /// </summary>
        public string Comments 
        {
            get { return comments; }
            set { comments = value; }
        }

        /// <summary>
        /// Parameters of the the transaction argument
        /// </summary>
        public NameValueCollection Parameters
        {
            get { return parameters; }
            set { parameters = value; }
        }

        /// <summary>
        /// State
        /// </summary>
        public MessageTransactionState State
        {
            get { return state; }
        }

        /// <summary>
        /// Exception
        /// </summary>
        public Exception Exception
        {
            get { return exception; }
        }

        /// <summary>
        /// Message transaction state succeeded
        /// </summary>
        public bool Succes
        {
            get { return state == MessageTransactionState.TransactionSucceeded; }
        }

        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        internal TransactionArgs()
        {
        }

        /// <summary>
        /// Set a new transaction state
        /// </summary>
        /// <param name="newstate"></param>
        internal void SetState(MessageTransactionState newstate)
        {
            if (newstate != MessageTransactionState.Unknown)
            {
                this.state = newstate;
            }
        }

        /// <summary>
        /// Set the exception and also the transaction to failed
        /// </summary>
        /// <param name="e"></param>
        internal void SetException(Exception e)
        {
            state = MessageTransactionState.TransactionFailed;
            exception = e;
        }
    }
}


